#pragma once
#include <iostream>
#include <sys/select.h>
#include "Socket.hpp"
#include "InetAddr.hpp"
#include "Log.hpp"

using namespace socket_ns;

class SelectServer
{
    const static int gnum = sizeof(fd_set) * 8;
    const static int gdefaultfd = -1;

public:
    SelectServer(uint16_t port) : _port(port), _listenSock(std::make_unique<TcpSocket>())
    {
        _listenSock->BuildTcpServerSocket(_port);
    }

    void Init()
    {
        for (int i = 0; i < gnum; ++i)
        {
            _fdArray[i] = gdefaultfd;
        }
        _fdArray[0] = _listenSock->Sockfd(); // 默认添加
    }

    // 处理新连接
    void Accepter()
    {
        InetAddr addr;
        int sockfd = _listenSock->Accepter(&addr);
        if (sockfd > 0)
        {
            LOG(INFO, "get a new link, client info %s:%d\n", addr.ip().c_str(), addr.port());
            // 将获取的sockfd添加到select中
            bool flag = false;
            for (int pos = 1; pos < gnum; ++pos)
            {
                if (_fdArray[pos] == gdefaultfd)
                {
                    flag = true;
                    _fdArray[pos] = sockfd;
                    LOG(INFO, "add %d in select success\n", sockfd);
                    break;
                }
            }
            if (!flag)
            {
                LOG(WARNING, "select is full!\n");
                ::close(sockfd);
            }
        }
    }

    // 处理普通fd
    void HandlerIO(int pos)
    {
        char buffer[1024];
        ssize_t n = ::recv(_fdArray[pos], buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "client say# " << buffer << std::endl;
            std::string content = "<html><body><h1>hello select</h1></body></html>";
            std::string echo_str = "HTTP/1.0 200 OK\r\n";
            echo_str += "Content-Type: text/html\r\n";
            echo_str += "Content-Length: " + std::to_string(content.size()) + "\r\n\r\n";
            echo_str += content;
            ::send(_fdArray[pos], echo_str.c_str(), echo_str.size(), 0);
        }
        else if (n == 0)
        {
            LOG(INFO, "client quit.\n");
            ::close(_fdArray[pos]);
            _fdArray[pos] = gdefaultfd;
        }
        else
        {
            LOG(INFO, "recv error!\n");
            ::close(_fdArray[pos]);
            _fdArray[pos] = gdefaultfd;
        }
    }

    // 事件派发
    void HandlerEvent(fd_set &rfds)
    {
        for (int i = 0; i < gnum; ++i)
        {
            if (_fdArray[i] == gdefaultfd)
                continue;
            if (FD_ISSET(_fdArray[i], &rfds))
            {
                if (_listenSock->Sockfd() == _fdArray[i])
                    Accepter();
                else
                    HandlerIO(i);
            }
        }
    }

    void Loop()
    {
        while (true)
        {
            // 1. 文件描述符初始化
            fd_set rfds;
            FD_ZERO(&rfds);
            int max_fd = gdefaultfd;

            // 2. 合法的fd添加到rfds中
            for (int i = 0; i < gnum; ++i)
            {
                if (_fdArray[i] == gdefaultfd)
                    continue;
                FD_SET(_fdArray[i], &rfds);
                if (max_fd < _fdArray[i])
                    max_fd = _fdArray[i];
            }

            // 3. 进行监听
            struct timeval timeout = {30, 0};
            int n = ::select(max_fd + 1, &rfds, nullptr, nullptr, &timeout);

            switch (n)
            {
            case 0:
                LOG(DEBUG, "time out, %d.%d\n", timeout.tv_sec, timeout.tv_usec);
                break;
            case -1:
                LOG(ERROR, "select error!\n");
                break;
            default:
                LOG(INFO, "haved event ready, n : %d\n", n);
                HandlerEvent(rfds);
                PrintDebug();
                break;
            }
        }
    }

    void PrintDebug()
    {
        std::cout << "fd list: ";
        for (int i = 0; i < gnum; i++)
        {
            if (_fdArray[i] == gdefaultfd)
                continue;
            std::cout << _fdArray[i] << " ";
        }
        std::cout << "\n";
    }

    ~SelectServer() {}

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listenSock;
    int _fdArray[gnum];
};